import _ from 'lodash';
import { generateRandomZipPath, logger } from '$common';
import { Schema } from '../validation/schema';
import { agentConfig } from '$domain/model/agent/config';
import { reportService } from '../service/reportService';
import { SimulationValidator } from '../validation';
import { simulationJobService } from '../service/jobService';
import { JobResultNotification, JobStatus, SimJobStatus, SimulationRequest, ValidateResult } from '../type';
import { jobRunnerCache } from '../simulationJobRunner';
import compressing from 'compressing';
import { Storage } from '$infra';

export class SimulationJobController {
  /**
   *  运行仿真任务，超时中断任务
   * @param request 
   */
  static async run(request: SimulationRequest) {
    logger.debug(`🍺 SimulationJobController ==> run, SimulationRequest: ${JSON.stringify(request, null, 2)}`);

    try {
      if (agentConfig.schemaValidation) {
        const simulationValidator = SimulationValidator.of(Schema.jobRequestSchema);
        const { error, value: verifiedRequest } = simulationValidator.validate(request) as ValidateResult;
        if (error) { throw new Error(`[${error.name}] ${error.message}`); }
        request = verifiedRequest;
      }

      const jobExist = simulationJobService.jobExist(request.id);

      if (!jobExist) {
        await simulationJobService.run(request);
      } else {
        logger.warn(`Idempotent interceptor. jobId: ${request.id}`);
      }
    } catch (e) {
      logger.error(`❌ Error message from the centralized error-handling component ===> ${e}`);
      const errorResponseBody = {
        id: request.id,
        success: false,
        message: e.message
      } as JobResultNotification;
      await reportService.jobResultReport(errorResponseBody);
    }
  }

  /**
   * 中断仿真任务
   * @param jobId 
   * @returns 
   */
  static async stop(jobId: string, modelBack: boolean = false) {
    logger.debug(`🍺 SimulationJobController ==> stop, jobId: ${jobId}, modelBack: ${modelBack}`);

    if (!jobId) {
      logger.info(`jobId: ${jobId} is not tracked.`);
      return;
    }

    await simulationJobService.stop(jobId);

    let simResultZipPath = '';
    if (modelBack) {
      const jobRunner = jobRunnerCache[jobId];
      const modelDir = jobRunner.simulationPath.getSimModelDir();
      const { zipPath: tmpLocalZipPath, randomDir } = generateRandomZipPath();
      simResultZipPath = `${jobRunner.storagePath.getSimResultPath(jobId)}/sim-result.zip`;
      logger.debug(`start zip ${modelDir} to ${simResultZipPath}`);
      await compressing.zip.compressDir(modelDir, tmpLocalZipPath);
      logger.debug(`start upload ${tmpLocalZipPath} to ${simResultZipPath}`);
      await Storage.Instance.fPutObject(simResultZipPath, tmpLocalZipPath);
    }

    await reportService.jobResultReport({
      id: jobId,
      success: false,
      message: 'Manually interrupt the job.',
      result: {
        simulationResultZip: simResultZipPath,
        specifiedParameters: []
      }
    });
  }

  /**
   *  重试仿真任务，超时中断任务
   * @param request
   */
  static async restart(request: SimulationRequest) {
    logger.debug(`🍺 SimulationJobController ==> restart, SimulationRequest: ${JSON.stringify(request, null, 2)}`);
    try {
      const simulationValidator = SimulationValidator.of(Schema.jobRequestSchema);
      const { error, value: verifiedRequest } = simulationValidator.validate(request) as ValidateResult;
      if (error) { throw new Error(`[${error.name}] ${error.message}`); }

      const jobExist = simulationJobService.jobExist(verifiedRequest.id);
      if (jobExist) {
        const preJobStatus = simulationJobService.getJobStatus(verifiedRequest.id);
        if (preJobStatus === JobStatus.Finished || preJobStatus === JobStatus.Interrupted) {
          await simulationJobService.run(verifiedRequest);
        } else {
          logger.warn(`restart job failed, pre job status = ${preJobStatus}`);
        }
      } else {
        logger.warn(`restart job(id=${verifiedRequest.id}), but pre job has not found`);
        await simulationJobService.run(verifiedRequest);
      }
    } catch (e) {
      logger.error(`❌ Error message from the centralized error-handling component ===> ${e}`);
      const errorResponseBody = {
        id: request.id,
        success: false,
        message: e.message
      } as JobResultNotification;
      await reportService.jobResultReport(errorResponseBody);
    }
  }

  static async stopProcessingJobs() {
    logger.debug('🍺 SimulationJobController ==> stopProcessingJobs');

    const processingJobs = simulationJobService.findNotFinishedJobs();

    if (!_.isEmpty(processingJobs)) {
      logger.debug(`⏸️ NotFinishedJobs: ${processingJobs.map(n => n.id)}`);
      for (const job of processingJobs) {
        await reportService.jobResultReport({
          id: job.id,
          success: false,
          message: 'Agent interrupted unexpectedly'
        });
      }
    }
  }

  static async queryJobStatus(ids: string[]) {
    const ret = {};
    console.log(`ids = ${JSON.stringify(ids)}`);
    ids.forEach(id => {
      const status = simulationJobService.getJobStatus(id) || SimJobStatus.UnKnown;
      ret[id] = status;
    });
    return ret;
  }
}